/**
 * see
 * https://github.com/smallalso/v-verify/blob/master/src/directive.js
 */
import Verify from './verify'
import errorRender from './error.js'
import vTips from './vtips/index.js'
import { filterRegParams, splitRegs, compareParams } from './utils'

export default class Directive extends Verify {
  constructor (Vue, config) {
    super(config.validators)
    Vue.validator = Vue.prototype.$validator = this
    this.config = config
    // 错误信息展示处理
    this.disError = errorRender(Vue, config)
    // 错误信息展示处理
    this.tipError = vTips(Vue, config)
  }

  /**
   * 执行事件
   * @param {*} value 自定义属性值
   * @param {*} options 返回的option
   * @returns bool
   */
  verifyRegs (value, options) {
    const {el, regs, name} = options
    // 劈分验证的属性 空,正则返回数组
    const _regs = splitRegs(regs)
    const _result = []
    if (!_regs) return
    for (let i = 0; i < _regs.length; i++) {
      const reg = _regs[i].trim()
      // reg 单个验证 具体值value  返回的结果bool
      if (this.verify(reg, value)) {
        _result.push(true)
        continue
      }
      _result.push(false)
      break
    }
    // _result 存的是验证是否通过的结果数组 [true, false, true]
    return this.dealVerifyResult(_result, _regs, options)
  }
  /**
   * 处理校验的结果
   * @param {*} _result 验证器返回的额结果
   * @param {*} _regs 验证器名称
   * @param {*} options 创建的options
   * @returns bool  false 校验没有通过
   */
  dealVerifyResult (_result, _regs, options) {
    // 获取最后一个校验结果 结果中有校验false 校验失败
    const _bool = !_result[_result.length - 1]
    const {bind, el, name} = options
    const _mode = options.mode || config.mode || 'insert'
    // 有错误对错误进行处理
    // 返回提示信息
    let _text = _bool ? this.getMessage(_regs[_result.length - 1].trim(), name) : ''
    if (_mode === 'insert') {
      // bind dom
      this.insertError(bind, _text, !_bool)
    } else if (_mode === 'tip') {
      this.tipsError(el, _text, !_bool)
    }
    // 添加错误类
    this.dealFormClass(_bool, options)
    return !_bool
  }
  /**
   * 插入错误提示信息
   * @param {*} el dom文档
   * @param {*} _text 错误文本
   * @param {*} _bool 错误标识
   */
  insertError (el, _text, _bool) {
    if (el.instance && el.instance.message === _text) return
    if (el.instance && _text !== '') {
      el.instance.message = _text
      return
    }

    el.instance = this.disError({
      el: el,
      target: el.instance || null,
      message: _text
    })
  }
  /**
   * 插入错误提示信息
   * @param {*} el 
   * @param {*} _text 
   * @param {*} _bool 
   */
  tipsError (el, _text, _bool) {
    if (el.instance && el.instance.message === _text) return
    if (el.instance && _text !== '') {
      el.instance.message = _text
      return
    }
    el.instance = this.tipError({
      el: el,
      remove: _bool,
      target: el.instance || null,
      message: _text
    })
  }
  /**
   * 添加错误信息类
   * @param {*} type true 校验没有通过
   * @param {*} options 
   */
  dealFormClass (type, options) {
    const { el, style } = options
    if (!style || (type && el.className.indexOf(style) !== -1)) return
    if (!type) {
      el.className = el.className.replace(style, '').replace(/\s+/gi, ' ')
      return
    }
    // 校验没有通过添加错误类
    el.className += ` ${style}`
  }

  /**
   * 提示信息
   * @param {*} reg 校验器名称
   * @param {*} value name 值
   */
  getMessage (reg, value) {
    const _reg = filterRegParams(reg)
    console.log(this.config.messages);
    const _msg = this.config.messages[_reg[0]]
    return _msg ? _msg(value, _reg[1]) : ''
  }

  /**
   * 绑定提交事件
   * @param {*} options 
   */
  bindSubmit (options) {
    const { el, submit } = options
    if (!submit) return
    const self = this
    this.addEvent(submit, () => {
      return self.verifyEvent(options)
    }) 
  }

  /**
   * 执行事件
   * @param {*} options 
   * @returns bool 
   */
  verifyEvent (options) {
    const { el } = options
    // 获取自定义属性 validate 的值
    const _setValue = el.dataset.validate
    // 自定义属性值覆盖value
    let _value = el.value
    if (_setValue !== 'null' && _setValue !== 'undefined' && _setValue) {
      _value = _setValue
    }
    return this.verifyRegs(_value, options)
  }

  /**
   * 绑定设置事件
   */
  bindEvent (options) {
    const { el, events } = options
    events.forEach(item => {
      if (item === 'initial') {
        return this.verifyEvent(options)
      }
      if (!this.isForm(el)) return
      // 执行事件
      el.addEventListener(item, (e) => {
        return this.verifyEvent(options)
      })
    })
  }

  setVerifyVal (el, val) {
    el.setAttribute('data-validate-val', val)
  }

  /**
   * 是否有 input textarea 标签存在判断是否是form表单
   */
  isForm (el) {
    if (!el) return
    let isForm = false
    const Form = ['input', 'textarea']
    if (Form.indexOf(el.tagName.toLowerCase()) > -1) {
      isForm = true
    }
    for (let i = 0; i < Form.length; i++) {
      if (el.querySelector(Form[i])) {
        isForm = true
        break
      }
    }
    return isForm
  }
  
  /**
   * 初始化校验
   */
  initVerify(el, binding, vnode) {
    const options = this.createOptions(el, binding);
    if (!this.isForm(el)) {
      vnode.data.props ? this.setVerifyVal(el, vnode.data.props.value) : null
    }
    this.bindSubmit(options)
    this.bindEvent(options)
  }

  /**
   * 创建我们所需要的Options
   */
  createOptions(el, binding) {
    // 获取指令绑定值得类型 text
    const _type = typeof binding.value === 'string'
    // 通过修复符获取执行的事件 on
    const _events = Object.keys(binding.modifiers)

    /**
     * 获取自定义属性
     * 返回
     * 绑定的元素
     * 绑定的dom
     * 正则
     * 样式
     * 提交
     * 事件
     */
    function generateParam (param) {
      /**
       *  指令绑定的值可以字符也可以是对象
       *  let param = 'name'
       *  let param = {
       *    name: '',
       *    style: '',
       *    mode: '',
       *    submit: '',
       *    regs: '$\d',
       *  }
       *  v-validate.on="param"
       */
      const data = _type ? el.getAttribute(`data-validate-${param}`) : binding.value[param]
      return data ? data : null
    }
    return {
      bind: el,
      el: el.querySelector('input') || el.querySelector('textarea') || el,
      regs: _type ? binding.value : binding.value.regs,
      name: generateParam('name') || '',
      style: generateParam('style') || this.config.errorForm || '',
      mode: generateParam('mode') || this.config.mode || null,
      submit: generateParam('submit'),
      events: _events.length ? _events : ['change']
    }
  }
  // // 添加指令
  install (Vue) {
    let _self = this;
    Vue.directive('validate', {
      // 被绑定元素插入父节点时调用
      inserted: function (el, binding, vnode) {
        // 初始化
        _self.initVerify(el, binding, vnode)
      },
      // 只调用一次，指令第一次绑定到元素时调用
      bind (el, binding, vnode, oldVnode) {
        let vm = vnode.context
        let field = binding.arg === 'EXTEND' ? el.getAttribute('name') : binding.arg
        let option = binding.modifiers
        let value = el.value
      },
      // 所在组件的 VNode 更新时调用
      update: function (el, binding, vnode, oldVnode) {
        // watch params change
        let options = null
        console.log(binding.value);
        if (!compareParams(binding.value, binding.oldValue)) {
          options = _self.initVerify(el, binding, vnode)
        }

        // verify v-model
        if (_self.isForm(el) || vnode.data.props.value === oldVnode.data.props.value) return
        options = options || _self.createOptions(el, binding)
        _self.setVerifyVal(el, vnode.data.props.value)
        _self.verifyEvent(options)
      },
      // 指令与元素解绑时调用
      unbind: function (el, binding) {
        const _type = typeof binding.value === 'string'
        const _submit = el.getAttribute(`data-validate-submit`) || binding.value.submit
        if (!_submit) return
        if (self.listener[_submit]) {
          self.removeEvent(_submit)
        }
      },
    })
  }
}